import "./index.less"
import Ui from "./ui"
import Uploader from "./uploader"

import { IconAddBorder, IconStretch, IconAddBackground, IconPicture, IconText, IconAlignLeft, IconAlignCenter, IconAlignRight } from "@codexteam/icons"

export default class ImageTool {
	static get isReadOnlySupported() {
		return true
	}
	static get toolbox() {
		return {
			icon: IconPicture,
			title: "Image",
		}
	}
	static get tunes() {
		return [
			{
				name: "alignLeft",
				icon: IconAlignLeft,
				title: "align Left",
				toggle: "list",
			},
			{
				name: "alignCenter",
				icon: IconAlignCenter,
				title: "align Center",
				toggle: "list",
			},
			{
				name: "alignRight",
				icon: IconAlignRight,
				title: "align Right",
				toggle: "list",
			},
			{
				name: "withBorder",
				icon: IconAddBorder,
				title: "With border",
				toggle: true,
			},
			{
				name: "stretched",
				icon: IconStretch,
				title: "Stretch image",
				toggle: true,
			},
			{
				name: "withBackground",
				icon: IconAddBackground,
				title: "With background",
				toggle: true,
			},
		]
	}
	/**
	 * Specify paste substitutes
	 * @see {@link https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling}
	 */
	static get pasteConfig() {
		return {
			/**
			 * Paste HTML into Editor
			 */
			tags: [
				{
					img: { src: true },
				},
			],
			/**
			 * Paste URL of image into the Editor
			 */
			patterns: {
				image: /https?:\/\/\S+\.(gif|jpe?g|tiff|png|svg|webp)(\?[a-z0-9=]*)?$/i,
			},

			/**
			 * Drag n drop file from into the Editor
			 */
			files: {
				mimeTypes: ["image/*"],
			},
		}
	}
	/**
	 * @param tool - tool properties got from editor.js
	 * @param tool.data - previously saved data
	 * @param tool.config - user config for Tool
	 * @param tool.api - Editor.js API
	 * @param tool.readOnly - read-only mode flag
	 * @param tool.block - current Block API
	 */
	constructor({ data, config, api, readOnly, block }) {
		this.api = api
		this.block = block

		/**
		 * Tool's initial config
		 */
		this.config = {
			endpoints: config.endpoints,
			additionalRequestData: config.additionalRequestData,
			additionalRequestHeaders: config.additionalRequestHeaders,
			field: config.field,
			types: config.types,
			captionPlaceholder: this.api.i18n.t(config.captionPlaceholder ?? "Caption"),
			buttonContent: config.buttonContent,
			uploader: config.uploader,
			actions: config.actions,
			features: config.features || {},
		}

		/**
		 * Module for file uploading
		 */
		this.uploader = new Uploader({
			config: this.config,
			onUpload: (response) => this.onUpload(response),
			onError: (error) => this.uploadingFailed(error),
		})

		/**
		 * Module for working with UI
		 */
		this.ui = new Ui({
			api,
			config: this.config,
			onSelectFile: () => {
				this.uploader.uploadSelectedFile({
					onPreview: (src) => {
						this.ui.showPreloader(src)
					},
				})
			},
			readOnly,
			styleConfig: {
				width: data.width || "",
				height: data.height || "",
			},
		})

		/**
		 * Set saved state
		 */
		this._data = {
			caption: "",
			withBorder: false,
			withBackground: false,
			stretched: false,
			width: "",
			height: "",
			file: {
				url: "",
			},
		}
		this.data = data
	}
	/**
	 * Renders Block content
	 */
	render() {
		if (this.config.features?.caption === true || this.config.features?.caption === undefined || (this.config.features?.caption === "optional" && this.data.caption)) {
			this.ui.applyTune("caption", true)
		}
		return this.ui.render(this.data)
	}
	/**
	 * Validate data: check if Image exists
	 * @param savedData — data received after saving
	 * @returns false if saved data is not correct, otherwise true
	 */
	validate(savedData) {
		return !!savedData.file.url
	}
	/**
	 * Return Block data
	 */
	save(blockContent) {
		const image = blockContent.querySelector(".image-tool__wrapper")
		const caption = this.ui.nodes.caption

		this._data.caption = caption.innerHTML
		this._data.width = image.style.width
		this._data.height = image.style.height
		return this.data
	}

	/**
	 * Returns configuration for block tunes: add background, add border, stretch image
	 * @returns TunesMenuConfig
	 */
	renderSettings() {
		// Merge default tunes with the ones that might be added by user
		// @see https://github.com/editor-js/image/pull/49
		const tunes = ImageTool.tunes.concat(this.config.actions || [])
		const featureTuneMap = {
			border: "withBorder",
			background: "withBackground",
			stretch: "stretched",
			caption: "caption",
		}

		if (this.config.features?.caption === "optional") {
			tunes.push({
				name: "caption",
				icon: IconText,
				title: "With caption",
				toggle: true,
			})
		}

		const availableTunes = tunes.filter((tune) => {
			const featureKey = Object.keys(featureTuneMap).find((key) => featureTuneMap[key] === tune.name)
			if (featureKey === "caption") {
				return this.config.features?.caption !== false
			}
			return featureKey == null || this.config.features?.[featureKey] !== false
		})

		return availableTunes.map((tune) => ({
			icon: tune.icon,
			label: this.api.i18n.t(tune.title),
			name: tune.name,
			toggle: tune.toggle,
			isActive: this.data[tune.name],
			onActivate: () => {
				/** If it'a user defined tune, execute it's callback stored in action property */
				if (typeof tune.action === "function") {
					tune.action(tune.name)

					return
				}
				this.tuneToggled(tune.name)
			},
		}))
	}
	/**
	 * Fires after clicks on the Toolbox Image Icon
	 * Initiates click on the Select File button
	 */
	appendCallback() {
		this.ui.nodes.fileButton.click()
	}
	/**
	 * Specify paste handlers
	 * @see {@link https://github.com/codex-team/editor.js/blob/master/docs/tools.md#paste-handling}
	 * @param event - editor.js custom paste event
	 *                              {@link https://github.com/codex-team/editor.js/blob/master/types/tools/paste-events.d.ts}
	 */
	async onPaste(event) {
		switch (event.type) {
			case "tag": {
				const image = event.detail.data

				/** Images from PDF */
				if (/^blob:/.test(image.src)) {
					const response = await fetch(image.src)

					const file = await response.blob()

					this.uploadFile(file)
					break
				}

				this.uploadUrl(image.src)
				break
			}
			case "pattern": {
				const url = event.detail.data

				this.uploadUrl(url)
				break
			}
			case "file": {
				const file = event.detail.file

				this.uploadFile(file)
				break
			}
		}
	}
	/**
	 * Private methods
	 * ̿̿ ̿̿ ̿̿ ̿'̿'\̵͇̿̿\з= ( ▀ ͜͞ʖ▀) =ε/̵͇̿̿/’̿’̿ ̿ ̿̿ ̿̿ ̿̿
	 */

	/**
	 * Stores all Tool's data
	 * @param data - data in Image Tool format
	 */
	set data(data) {
		this.image = data.file
		this._data.caption = data.caption || ""
		this.ui.fillCaption(this._data.caption)
		ImageTool.tunes.forEach(({ name: tune }) => {
			const value = typeof data[tune] !== "undefined" ? data[tune] === true || data[tune] === "true" : false
			this.setTune(tune, value)
		})
	}
	/**
	 * Return Tool data
	 */
	get data() {
		return this._data
	}
	/**
	 * Set new image file
	 * @param file - uploaded file data
	 */
	set image(file) {
		this._data.file = file || { url: "" }

		if (file && file.url) {
			this.ui.fillImage(file.url)
		}
	}
	/**
	 * File uploading callback
	 * @param response - uploading server response
	 */
	onUpload(response) {
		if (response.success && Boolean(response.file)) {
			this.image = response.file
		} else {
			this.uploadingFailed("incorrect response: " + JSON.stringify(response))
		}
	}
	/**
	 * Handle uploader errors
	 * @param errorText - uploading error info
	 */
	uploadingFailed(errorText) {
		console.log("Image Tool: uploading failed because of", errorText)

		this.api.notifier.show({
			message: this.api.i18n.t("Couldn’t upload image. Please try another."),
			style: "error",
		})
		this.ui.hidePreloader()
	}
	/**
	 * Callback fired when Block Tune is activated
	 * @param tuneName - tune that has been clicked
	 */
	tuneToggled(tuneName) {
		// inverse tune state
		this.setTune(tuneName, !this._data[tuneName])

		// reset caption on toggle
		if (tuneName === "caption" && !this._data[tuneName]) {
			this._data.caption = ""
			this.ui.fillCaption("")
		}
	}
	/**
	 * Set one tune
	 * @param tuneName - {@link Tunes.tunes}
	 * @param value - tune state
	 */
	setTune(tuneName, value) {
		const alignArr = ["alignLeft", "alignCenter", "alignRight"]
		let isAlignment = alignArr.includes(tuneName)
		if (isAlignment && value) {
			// 先将所有对齐相关的属性置为false
			alignArr.forEach((alignment) => {
				this._data[alignment] = false
				this.setTune(alignment, false)
			})
			this._data[tuneName] = value
			this.ui.applyTune(tuneName, value)
		} else if (isAlignment) {
			// 如果只是单纯传入了对齐相关的属性名，但值为false这种情况不做处理 保证激活的对齐样式不会被取消
		} else {
			this._data[tuneName] = value
			this.ui.applyTune(tuneName, value)
		}
		if (tuneName === "stretched") {
			/**
			 * Wait until the API is ready
			 */
			Promise.resolve()
				.then(() => {
					this.block.stretched = value
				})
				.catch((err) => {
					console.error(err)
				})
		}
	}
	/**
	 * Show preloader and upload image file
	 * @param file - file that is currently uploading (from paste)
	 */
	uploadFile(file) {
		this.uploader.uploadByFile(file, {
			onPreview: (src) => {
				this.ui.showPreloader(src)
			},
		})
	}

	/**
	 * Show preloader and upload image by target url
	 * @param url - url pasted
	 */
	uploadUrl(url) {
		this.ui.showPreloader(url)
		this.uploader.uploadByUrl(url)
	}
}

